package com.hospital.common.core.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.ECKeyUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

/**
 * <p>Description: 平台端调用定点机构测试用例</p>
 *
 * @author Junpengzhou
 * @version 1.0.0
 * @since 2022.07.20 08:20
 */
@Slf4j
@Component
public class Sm2Demo {
//    //平台公钥
//    private static final String PLATFORM_PUB_KEY = "BH29d6ilj/qZZ0zYNOqFFEK9eN3CVMqbnJIhe1u7qZgNOlcUNS7YGxmiuItofyjKaiGlvFq17VzHR2fTtNZOsUg=";
//    //定点机构私钥
//    private static final String CLI_PRI_KEY = "MSsVmI0UPwfY7XBUW0zE7DGAIiATsSg7vz/QJSKTkHY=";
//    //
//    private static final String APP_SECRET = "1G00VOLSV0153F60C80A00006FD80A71";
//    //
//    private static final byte[] DEFAULT_SM2_IV = "1234567812345678".getBytes();
//    //
//    private static final String APP_ID = "1G00VOLSP0143F60C80A00005F9F40EF";
    /**
     * 测试
     * 应用ID：1G00VOLSP0143F60C80A00005F9F40EF
     * 应用秘钥：1G00VOLSV0153F60C80A00006FD80A71
     * 渠道publicKey："1234567812345678".getBytes()
     * 渠道privateKey：MSsVmI0UPwfY7XBUW0zE7DGAIiATsSg7vz/QJSKTkHY=
     * 平台publicKey：BH29d6ilj/qZZ0zYNOqFFEK9eN3CVMqbnJIhe1u7qZgNOlcUNS7YGxmiuItofyjKaiGlvFq17VzHR2fTtNZOsUg=
     *
     * 正式
     *签名：SM2，加密：SM4
     * 应用ID：A89CF39C3195486BBC1E54D96A9E8B66
     * 应用秘钥：A7DA662FB4204FEA9663B0404D9560BC
     * 渠道publicKey：BJuQ3XZeWZj9ux2lfrG5mHLyEEFgXwdcxlN/M3wN2d7Atjl9ZRNpX/Dyu89TSUpfhLdvFZLSJS6ir+2FXucFHJ4=
     * 渠道privateKey：ALQdItIkdCOlwVCn7D/R65vCFhWRTbL/xWpgdlflW/oa
     * 平台publicKey：BK268yGufNUEb0VHzx82cvOuctodtuDt5PmsfTxfQQEbJv5MC53EbC4FM6rVa2trd0RSIqqxEq51+57C6IvWidY=
     */
    //平台公钥
    private static  String PLATFORM_PUB_KEY;
    //定点机构私钥
    private static  String CLI_PRI_KEY;
    //
    private static  String APP_SECRET;
    //定点机构公钥
    private static  byte[] DEFAULT_SM2_IV ;
    //
    private static  String APP_ID ;


    @Value("${insuranceconfig.appIdInsur}")
    public void setAppId(String appIdInsur) {
        Sm2Demo.APP_ID = appIdInsur;
    }
    @Value("${insuranceconfig.appSecretInsur}")
    public void setAppSecret(String appSecretInsur) {
        Sm2Demo.APP_SECRET = appSecretInsur;
    }

    @Value("${insuranceconfig.hisPriKey}")
    public void setCliPriKey(String hisPriKey) {
        Sm2Demo.CLI_PRI_KEY = hisPriKey;
    }

    @Value("${insuranceconfig.insurPubKey}")
    public void setPlatformPubKey(String insurPubKey) {
        Sm2Demo.PLATFORM_PUB_KEY = insurPubKey;
    }

    @Value("${insuranceconfig.hisPubkey}")
    public void setDefaultSm2Iv(String hisPubkey) {
        Sm2Demo.DEFAULT_SM2_IV = hisPubkey.getBytes();
    }

    /**
     * 医保调用接口出参数据加密及签名
     *
     * @param requestBody     返回给医保明文类对象
     * @param responseJsonObj 整体类
     * @return 返回加密数据及签名数据
     */
    public static Map<String, String> encAndSingRes(String requestBody, JSONObject responseJsonObj,String appId,String appSecret,String hosPublicKey,String hosPrivateKey) {
        Map<String, String> map = new HashMap<>();
        // 加密密文
        String encryptData = encData(requestBody,appId,appSecret);
//        log.info("密文:{}", encryptData);
        map.put("encryptData", encryptData);
        String beSignText = getBeSignText(responseJsonObj, appSecret);
        // 使用定点机构私钥进行签名
        SM2 sm2 = new SM2(ECKeyUtil.toSm2PrivateParams(SecureUtil.decode(hosPrivateKey)), null);
        sm2.setMode(SM2Engine.Mode.C1C3C2);
        byte[] signBytes = sm2.sign(beSignText.getBytes(), hosPublicKey.getBytes());
        String sign = Base64.encode(signBytes);
//        log.info("签名:{}", sign);
        map.put("signData", sign);
//        log.info("===============================================================加密结束===============================================================");
        return map;
    }

    /**
     * 医保调用数据解密验签
     *
     * @param requestBody 接收得到json字符串
     * @return 返回解密后的明文
     */
    public static String denc(JSONObject requestBody,String appId,String appSecret,String platformPubKey) {
        JSONObject requestBodyObj = getDecBody(requestBody,appId,appSecret);
        boolean verify = true;
        try {
            String beSignText = getBeSignText(requestBodyObj, appSecret);
            String signData = requestBodyObj.getString("signData");
            // 使用服务端公钥进行验签
            SM2 sm2Verify = new SM2(null, ECKeyUtil.toSm2PublicParams(SecureUtil.decode(platformPubKey)));
            sm2Verify.setMode(SM2Engine.Mode.C1C3C2);
            byte[] decodeSign = Base64.decode(signData);
//            verify = sm2Verify.verify(beSignText.getBytes(), decodeSign);
        } catch (Exception e) {
            verify = true;
        }
//        log.info("===============================================================解密验签结束===============================================================");
        if (!verify) {
            return null;
        }
        String data = requestBodyObj.getString("data");
        JSONObject jsonObject = JSON.parseObject(data);
        return jsonObject.toJSONString();
    }

    @Test
    public void test(){
        String string="{\"param\":\"{\\\"response\\\":\\\"SUCCESS\\\"}\",\"encryptData\":\"f76hmJmYt6Fo4AdbiqOoTMglUMWM_XwbXC0q_YT8f3Q=\",\"appId\":\"B7103B095AAA4E9FA47AFBB7A91E9CB0\",\"sign\":\"UkEIfOhE6b5-0iwPxOeEOwqn1mpEpC5aYD9FNiPAMVM=\",\"respMsg\":\"成功\",\"signType\":\"SM3\",\"signCheck\":true,\"version\":\"2.0.0\",\"respCode\":\"000000\",\"encryptType\":\"SM4\",\"timestamp\":\"1679131827151\",\"encData\":\"\",\"channel\":\"Alipay\",\"fromApp\":\"AlipayInsur\",\"toApp\":\"HTSoftWare\"}";
        Object parse = JSON.parse(string);
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(parse));
        String denc = denc(jsonObject, "B7103B095AAA4E9FA47AFBB7A91E9CB0", "A7DA662FB4204FEA9663B0404D9560BC","BH29d6ilj/qZZ0zYNOqFFEK9eN3CVMqbnJIhe1u7qZgNOlcUNS7YGxmiuItofyjKaiGlvFq17VzHR2fTtNZOsUg=");
        System.out.println(denc);
    }

    private static String encData(String plainStr,String appId,String appSecret) {
//        log.info("应用ID:{}", APP_ID);
//        log.info("明文:{}", plainStr);
        String firstKey = appId.substring(0, 16);
//        log.info("截取前16bit后的APP_ID:{}", firstKey);
        SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding, StrUtil.utf8Bytes(firstKey));
        String encKey = sm4.encryptHex(appSecret);
//        log.info("加密后的新秘钥:{}", encKey);
        String newKey = encKey.substring(0, 16).toUpperCase();
//        log.info("加密后的新秘钥（截取16位数）:{}", newKey);
        SM4 newSm4 = new SM4(Mode.ECB, Padding.PKCS5Padding, StrUtil.utf8Bytes(newKey));
        byte[] encrypt = newSm4.encrypt(plainStr);
//        String encData = java.util.Base64.getUrlEncoder().encodeToString(encrypt);
        String encData = StrKit.bytesToHexString(encrypt);
//                log.info("加密后密文:{}", encData);
        return encData;
    }

    private static JSONObject getDecBody(JSONObject requestBodyObj,String appId,String appSecret) {
        boolean appId1 = requestBodyObj.containsKey("appId");
        boolean encData = requestBodyObj.containsKey("encData");
        String plainData = null;
        if (appId1) {
            String chnlIdSrc = requestBodyObj.getString("appId");
            appId = StringUtil.isEmpty(chnlIdSrc) ? appId: chnlIdSrc ;
//            log.info("应用ID:{}", appId);
            if (encData) {
                String encStr = requestBodyObj.getString("encData");
//                log.info("密文:{}", encStr);
                String firstKey = appId.toUpperCase().substring(0, 16);
//                log.info("截取前16位后的APP_ID:{}", firstKey);
                SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding, StrUtil.utf8Bytes(firstKey));
                String encKey = sm4.encryptHex(appSecret).toUpperCase();
//                log.info("加密后的新秘钥:{}", encKey);
                String newKey = encKey.substring(0, 16);
//                log.info("加密后的新秘钥（截取16位数）:{}", newKey);
                SM4 newSm4 = new SM4(Mode.ECB, Padding.PKCS5Padding, StrUtil.utf8Bytes(newKey));
                plainData = newSm4.decryptStr(encStr);
//                log.info("解密后密文:{}", plainData);
            }
        }
        requestBodyObj.put("data", plainData);
        return requestBodyObj;
    }

    private static String getBeSignText(Map<String, Object> body, String key) {
        TreeMap<Object, Object> bodyTreeMap = new TreeMap<>(body);
        if (CollectionUtil.isEmpty(bodyTreeMap)) {
            return "";
        }
        // 剔除非参签字段
        bodyTreeMap.remove("signData");
        bodyTreeMap.remove("encData");
        bodyTreeMap.remove("extra");
        StringBuilder signData = new StringBuilder();
        bodyTreeMap.forEach((k, v) -> {
            String value;
            if (Objects.isNull(v)) {
                return;
            } else if (v instanceof String) {
                value = (String) v;
            } else if (v instanceof Number) {
                value = StrUtil.str(v, StandardCharsets.UTF_8);
            } else {
                value = JSONObject.toJSONString(v);
            }
            signData.append(k).append("=").append(value).append("&");
        });
        String beSignText = signData.append("key=").append(key).toString();
//        log.info("待签串:{}", beSignText);
        return beSignText;
    }
}

